/**
 * Created by PanJiaChen on 16/11/18.
 */
import Vue from 'vue'
import defaultSettings from '@/settings'

export function objAssign(objA, objB) {
  Object.keys(objA).forEach((key) => {
    if (Object.prototype.hasOwnProperty.call(objB, key)) {
      objA[key] = objB[key]
    }
  })
}
// //把数组B的值赋值给A
export function arrAssign(arrA, arrB) {
  arrA.length = 0
  arrB.forEach((item) => {
    arrA.push(deepClone(item))
  })
}
export function deepClone(target, hash = new WeakMap()) {
  // 额外开辟一个存储空间WeakMap来存储当前对象
  if (target === null) return target // 如果是 null 就不进行拷贝操作
  if (target instanceof Date) return new Date(target) // 处理日期
  if (target instanceof RegExp) return new RegExp(target) // 处理正则
  if (target instanceof HTMLElement) return target // 处理 DOM元素
  if (typeof target !== 'object') return target // 处理原始类型和函数 不需要深拷贝，直接返回
  // 是引用类型的话就要进行深拷贝
  if (hash.get(target)) return hash.get(target) // 当需要拷贝当前对象时，先去存储空间中找，如果有的话直接返回
  const cloneTarget = new target.constructor() // 创建一个新的克隆对象或克隆数组
  hash.set(target, cloneTarget) // 如果存储空间中没有就存进 hash 里

  Reflect.ownKeys(target).forEach((key) => {
    // 引入 Reflect.ownKeys，处理 Symbol 作为键名的情况
    cloneTarget[key] = deepClone(target[key], hash) // 递归拷贝每一层
  })
  return cloneTarget // 返回克隆的对象
}

/**
 * Parse the time to string
 * @param {(Object|string|number)} time
 * @param {string} cFormat
 * @returns {string | null}
 */
export function parseTime(time, cFormat) {
  if (arguments.length === 0 || !time) {
    return null
  }
  const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
  let date
  if (typeof time === 'object') {
    date = time
  } else {
    if (typeof time === 'string') {
      if (/^[0-9]+$/.test(time)) {
        // support "1548221490638"
        time = parseInt(time)
      } else {
        // support safari
        // https://stackoverflow.com/questions/4310953/invalid-date-in-safari
        time = time.replace(new RegExp(/-/gm), '/')
      }
    }

    if (typeof time === 'number' && time.toString().length === 10) {
      time = time * 1000
    }
    date = new Date(time)
  }
  const formatObj = {
    y: date.getFullYear(),
    m: date.getMonth() + 1,
    d: date.getDate(),
    h: date.getHours(),
    i: date.getMinutes(),
    s: date.getSeconds(),
    a: date.getDay()
  }
  const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
    const value = formatObj[key]
    // Note: getDay() returns 0 on Sunday
    if (key === 'a') {
      return ['日', '一', '二', '三', '四', '五', '六'][value]
    }
    return value.toString().padStart(2, '0')
  })
  return time_str
}

/**
 * 计算两个日期之间的差值
 */
export function getDiffDay(date_1, date_2) {
  let totalDays, diffDate
  let myDate_1 = Date.parse(date_1)
  let myDate_2 = Date.parse(date_2)
  // 将两个日期都转换为毫秒格式，然后做差
  diffDate = Math.abs(myDate_1 - myDate_2) // 取相差毫秒数的绝对值

  totalDays = Math.floor(diffDate / (1000 * 3600 * 24)) // 向下取整

  return totalDays // 相差的天数
}
/**
 * @param {number} time
 * @param {string} option
 * @returns {string}
 */
export function formatTime(time, option) {
  if (('' + time).length === 10) {
    time = parseInt(time) * 1000
  } else {
    time = +time
  }
  const d = new Date(time)
  const now = Date.now()

  const diff = (now - d) / 1000

  if (diff < 30) {
    return '刚刚'
  } else if (diff < 3600) {
    // less 1 hour
    return Math.ceil(diff / 60) + '分钟前'
  } else if (diff < 3600 * 24) {
    return Math.ceil(diff / 3600) + '小时前'
  } else if (diff < 3600 * 24 * 2) {
    return '1天前'
  }
  if (option) {
    return parseTime(time, option)
  } else {
    return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'
  }
}
export function debounce(func, wait, immediate) {
  let timeout, args, context, timestamp, result

  const later = function () {
    // 据上一次触发时间间隔
    const last = +new Date() - timestamp

    // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
    if (last < wait && last > 0) {
      timeout = setTimeout(later, wait - last)
    } else {
      timeout = null
      // 如果设定为immediate===true，因为开始边界已经调用过了此处无需调用
      if (!immediate) {
        result = func.apply(context, args)
        if (!timeout) context = args = null
      }
    }
  }

  return function (...args) {
    context = this
    timestamp = +new Date()
    const callNow = immediate && !timeout
    // 如果延时不存在，重新设定延时
    if (!timeout) timeout = setTimeout(later, wait)
    if (callNow) {
      result = func.apply(context, args)
      context = args = null
    }

    return result
  }
}
/**
 * @param {string} url
 * @returns {Object}
 */
export function param2Obj(url) {
  const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
  if (!search) {
    return {}
  }
  const obj = {}
  const searchArr = search.split('&')
  searchArr.forEach((v) => {
    const index = v.indexOf('=')
    if (index !== -1) {
      const name = v.substring(0, index)
      const val = v.substring(index + 1, v.length)
      obj[name] = val
    }
  })
  return obj
}

// 随机生成指定长度的字符串
export function randomString(length) {
  const str = 'abcdefghijklmnopqrstuvwxyz9876543210'
  let tmp = ''
  let i = 0
  const l = str.length
  for (i = 0; i < length; i++) {
    tmp += str.charAt(Math.floor(Math.random() * l))
  }
  return tmp
}

/**
 * 数组对象去重
 * @param {array} arr 去重数组
 * @param {string} name 属性
 * @returns {array}
 */
export function unique(arr, name) {
  const obj = {}
  arr = arr.reduce(function (a, b) {
    obj[b[name]] ? '' : (obj[b[name]] = true && a.push(b))
    return a
  }, [])
  return arr
}

/**
 * 一维数组转树形数组
 * @param {array} data 一维数组
 * @param {string} id
 * @param {string} parent_id
 * @param {string} children
 * @returns {array}
 */
export function toTree(data, id = 'id', parent_id = 'parent_id', children = 'children') {
  const result = []
  if (!Array.isArray(data)) {
    return result
  }
  data.forEach((item) => {
    delete item[children]
  })
  const map = {}
  data.forEach((item) => {
    map[item[id]] = item // id为键，原数据每一项为值的map对象
  })
  data.forEach((item) => {
    const parent = map[item[parent_id]] // item的parent_id若与map对象的键相同，则item为父级节点
    if (parent) {
      ;(parent[children] || (parent[children] = [])).push(item)
    } else {
      result.push(item)
    }
  })
  return result
}
/**
 * 树形数组转一维数组
 * @param {array} treeObj
 * @param {string} children
 * @returns {array}
 */
export function treeToList(treeObj, children = 'children') {
  const res = [] // 用于存储递归结果（扁平数据）
  // 递归函数
  const fn = (source) => {
    source.forEach((el) => {
      res.push(el)
      el[children] && el[children].length > 0 ? fn(el[children]) : '' // 子级递归
      delete el[children]
    })
  }
  fn(treeObj)
  return res
}

// 深拷贝
export function deepCopy(obj) {
  // 深度复制数组
  if (Object.prototype.toString.call(obj) === '[object Array]') {
    const object = []
    for (let i = 0; i < obj.length; i++) {
      object.push(deepCopy(obj[i]))
    }
    return object
  }
  // 深度复制对象
  if (Object.prototype.toString.call(obj) === '[object Object]') {
    const object = {}
    for (const p in obj) {
      object[p] = obj[p]
    }
    return object
  }
}

// 复制函数
export function copy(text) {
  const input = document.createElement('input')
  input.setAttribute('readonly', 'readonly') // 防止手机上弹出软键盘
  input.setAttribute('value', text)
  document.body.appendChild(input)
  input.select()
  const res = document.execCommand('copy')
  document.body.removeChild(input)
  return res
}

// 加
export function floatAdd(arg1, arg2) {
  var r1, r2, m
  try {
    r1 = arg1.toString().split('.')[1].length
  } catch (e) {
    r1 = 0
  }
  try {
    r2 = arg2.toString().split('.')[1].length
  } catch (e) {
    r2 = 0
  }
  m = Math.pow(10, Math.max(r1, r2))
  return (arg1 * m + arg2 * m) / m
}

// 减
export function floatSub(arg1, arg2) {
  var r1, r2, m, n
  try {
    r1 = arg1.toString().split('.')[1].length
  } catch (e) {
    r1 = 0
  }
  try {
    r2 = arg2.toString().split('.')[1].length
  } catch (e) {
    r2 = 0
  }
  m = Math.pow(10, Math.max(r1, r2))
  // 动态控制精度长度
  n = r1 >= r2 ? r1 : r2
  return ((arg1 * m - arg2 * m) / m).toFixed(n)
}

// 乘
export function floatMul(arg1, arg2) {
  var m = 0
  var s1 = arg1.toString()
  var s2 = arg2.toString()
  try {
    m += s1.split('.')[1].length
  } catch (e) {}
  try {
    m += s2.split('.')[1].length
  } catch (e) {}
  return (Number(s1.replace('.', '')) * Number(s2.replace('.', ''))) / Math.pow(10, m)
}

// 除
export function floatDiv(arg1, arg2) {
  var t1 = 0
  var t2 = 0
  var r1
  var r2
  try {
    t1 = arg1.toString().split('.')[1].length
  } catch (e) {}
  try {
    t2 = arg2.toString().split('.')[1].length
  } catch (e) {}

  r1 = Number(arg1.toString().replace('.', ''))

  r2 = Number(arg2.toString().replace('.', ''))
  return (r1 / r2) * Math.pow(10, t2 - t1)
}
export function isElementInViewport(el, container = null) {
  // 获取元素是否在可视区域
  if (!el || !container) return false

  const elRect = el.getBoundingClientRect()
  let containerRect

  if ([window, document, document.documentElement, null, undefined].includes(container)) {
    containerRect = {
      top: 0,
      right: window.innerWidth,
      bottom: window.innerHeight,
      left: 0
    }
  } else {
    containerRect = container.getBoundingClientRect()
  }
  return (
    elRect.top < containerRect.bottom &&
    elRect.bottom > containerRect.top &&
    elRect.right > containerRect.left &&
    elRect.left < containerRect.right
  )
}

/**
 * 使用动画滚动导航部分
 * @param el {Element} 需要滚动的元素
 * @param start {Number} 滚动开始的位置
 * @param end {Number} 滚动结束的位置
 * @param step {Number} 每一步滚动的长度
 */
export function scrollAnimation(el, start, end, step) {
  // step = step > 360 ? 360 : step
  // 向右滑动的逻辑
  if (start < end) {
    start = start + step
    // 要是剩下的不够一步就直接撑满，防止页面出现滚动偏移错误
    if (start > end) {
      start = end
    }
  }
  // 向左滑动的逻辑
  if (start > end) {
    start = start - step
    if (start < end) {
      start = end
    }
  }
  el.scrollTop = start
  // el.scrollTo(start, 0)
  // 要是开始和结束是一样的就不会安排下一次的滚动了
  // 在高刷新的屏幕上会有运行的比较块
  if (start !== end) {
    requestAnimationFrame(() => {
      scrollAnimation(el, start, end, step)
    })
  }
}

// 计算字符串长度
export function getStrLength(str) {
  let len = 0

  for (let i = 0; i < str.length; i++) {
    const c = str.charCodeAt(i)

    // 单字节加1

    if ((c >= 0x0001 && c <= 0x007e) || (c >= 0xff60 && c <= 0xff9f)) {
      len++
    } else {
      len += 2
    }
  }
  return len
}
export function isJSON(str) {
  if (typeof str == 'string') {
    try {
      var obj = JSON.parse(str)
      if (typeof obj == 'object' && obj) {
        return true
      } else {
        return false
      }
    } catch (e) {
      return false
    }
  }
}
export function isXML(xmlString) {
  // 创建 DOMParser 实例
  const parser = new DOMParser()
  // 解析 XML 字符串，返回一个 Document 对象
  const xmlDoc = parser.parseFromString(xmlString, 'text/xml')
  // 检查是否有解析错误
  const errors = xmlDoc.getElementsByTagName('parsererror')
  if (errors.length > 0) {
    // 输出错误信息
    return false
  } else {
    // 输出解析结果
    return true
  }
}
export function generateUUID() {
  let d = new Date().getTime()
  if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
    d += performance.now() // use high-precision timer if available
  }
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    const r = (d + Math.random() * 16) % 16 | 0
    d = Math.floor(d / 16)
    return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16)
  })
}
export function findNode(data, prop, val, result) {
  data.forEach((item) => {
    if (item[prop] === val) {
      result.push(item)
      return false
    }
    if (item.children && item.children.length > 0) {
      findNode(item.children, prop, val, result)
    }
    return true
  })
  return result[0]
}

export function receivedMessage(callback) {
  if (defaultSettings.isWPF) {
    let receivedMessageOperation = defaultSettings.isOnlineWpf ? window : window.chrome.webview
    receivedMessageOperation.addEventListener('message', function (result) {
      // 判断消息是否为期望的消息
      callback(result)
    })
  } else if (defaultSettings.isCEF) {
    Vue.prototype.QWebChannelSendObject.sendMessage.connect(function (e) {
      // 处理接收到的数据
      callback(e)
    })
  }
}

export function postMessage(message) {
  if (defaultSettings.isWPF) {
    if (defaultSettings.isOnlineWpf) {
      parent.postMessage(message, '*')
    } else {
      window.chrome.webview.postMessage(message)
    }
  } else {
    setTimeout(() => {
      Vue.prototype.QWebChannelSendObject.receiveMessage(JSON.stringify(message))
    })
  }
}
